package com.strp.mptcp.socket;

import com.strp.mptcp.common.MPTCPMetrics;
import com.strp.mptcp.congestion.CongestionController;
import com.strp.mptcp.path.PathManager;
import com.strp.mptcp.scheduler.Scheduler;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * MultipathSocket：多路径上传客户端主类
 * - 负责初始化 Netty EventLoopGroup、SSL 上下文
 * - 负责创建 PathManager、CongestionController、Scheduler
 * - 提供文件上传（支持断点续传）接口
 * - 关闭时优雅停掉各模块
 *
 * @author 曹行阳
 * @date 2025/6/12
 */
@Component
public class MultipathSocket {
    private EventLoopGroup eventLoopGroup;
    private SslContext sslContext;
    private PathManager pathManager;
    private CongestionController congestionController;
    private Scheduler scheduler;
    private MPTCPMetrics metrics;

    private int chunkSize;
    private int maxRetries;
    private int connectionTimeout;
    private int readTimeout;
    private int writeTimeout;

    private static final int SHUTDOWN_TIMEOUT_SECONDS = 5;

    public MultipathSocket() {
    }

    public MultipathSocket(List<SocketAddress> serverAddresses, MPTCPMetrics metrics) throws Exception {
        this(serverAddresses, metrics, 1048576, 3, 30000, 30000, 30000); // 默认值
    }

    public MultipathSocket(List<SocketAddress> serverAddresses, MPTCPMetrics metrics,
                           int chunkSize, int maxRetries, int connectionTimeout,
                           int readTimeout, int writeTimeout) throws Exception {
        this.metrics = metrics;
        this.chunkSize = chunkSize;
        this.maxRetries = maxRetries;
        this.connectionTimeout = connectionTimeout;
        this.readTimeout = readTimeout;
        this.writeTimeout = writeTimeout;

        // 1. 初始化 Netty 的 EventLoopGroup
        this.eventLoopGroup = new NioEventLoopGroup();

        // 2. 初始化 SSLContext
        this.sslContext = SslContextBuilder.forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                .protocols("TLSv1.2", "TLSv1.3")
                .build();

        // 3. 初始化拥塞控制器
        this.congestionController = new CongestionController();

        // 4. 初始化 PathManager
        this.pathManager = new PathManager(eventLoopGroup, sslContext);

        // 5. 初始化 Scheduler
        this.scheduler = new Scheduler(pathManager, congestionController, metrics);

        // 6. 启动 Scheduler
        this.scheduler.start();

        // 7. 创建并连接所有子流
        connectWithRetry(serverAddresses);
    }

    private void connectWithRetry(List<SocketAddress> addresses) {
        int attempts = 0;
        while (attempts < maxRetries) {
            try {
                this.pathManager.createAndConnectPaths(addresses);
                return;
            } catch (Exception e) {
                attempts++;
                if (attempts == maxRetries) {
                    throw new RuntimeException("Failed to connect after " + maxRetries + " attempts", e);
                }
                try {
                    Thread.sleep(1000); // 1秒重试延迟
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Connection interrupted", ie);
                }
            }
        }
    }

    /**
     * 下载文件
     *
     * @param file   要保存的文件
     * @param offset 起始偏移量（字节）
     */
    public void downloadFile(File file, long offset) {
        // 参数校验
        if (file == null || offset < 0) {
            throw new IllegalArgumentException("Invalid file or offset for download. file="
                    + file + ", offset=" + offset);
        }

        // 生成文件传输ID
        String transferId = UUID.randomUUID().toString();

        // 记录开始时间
        long startTime = System.currentTimeMillis();

        try {
            // 确保目标文件存在
            if (!file.exists()) {
                file.createNewFile();
            }

            // 将文件下载任务交给 Scheduler 处理
            scheduler.downloadFile(file, offset, transferId);

            // 记录传输完成时间
            long endTime = System.currentTimeMillis();
            metrics.recordLatency(0, endTime - startTime); // 使用0作为全局传输ID
        } catch (IOException e) {
            throw new RuntimeException("Failed to create download file", e);
        }
    }

    /**
     * 上传整个文件，从头开始
     */
    public void send(File file) {
        resumeFromOffset(file, 0L, null);
    }

    /**
     * 支持断点续传：从指定偏移量开始上传
     *
     * @param sourceFile 要上传的源文件
     * @param offset    起始偏移量（字节）
     * @param targetFile 目标文件（可选，如果为null则使用源文件名）
     */
    public void resumeFromOffset(File sourceFile, long offset, File targetFile) {
        // 参数校验
        if (sourceFile == null || !sourceFile.exists() || offset < 0 || offset >= sourceFile.length()) {
            throw new IllegalArgumentException("Invalid file or offset for resume. file="
                    + sourceFile + ", offset=" + offset);
        }

        // 生成文件传输ID
        String transferId = UUID.randomUUID().toString();

        // 记录开始时间
        long startTime = System.currentTimeMillis();

        try {
            // 如果目标文件未指定，使用源文件名
            if (targetFile == null) {
                targetFile = new File(sourceFile.getName());
            }

            // 确保目标文件存在
            if (!targetFile.exists()) {
                targetFile.createNewFile();
            }

            // 将实际的文件和偏移量交给 Scheduler 去切片、调度
            scheduler.uploadFile(sourceFile, targetFile, offset, transferId);

            // 记录传输完成时间
            long endTime = System.currentTimeMillis();
            metrics.recordLatency(0, endTime - startTime); // 使用0作为全局传输ID
        } catch (IOException e) {
            throw new RuntimeException("Failed to create target file", e);
        }
    }

    /**
     * 获取文件传输进度
     */
    public double getTransferProgress(String transferId) {
        return metrics.getFileProgress(transferId);
    }

    /**
     * 获取子流统计信息
     */
    public MPTCPMetrics.SubflowStats getSubflowStats(int subflowId) {
        return metrics.getSubflowStats(subflowId);
    }

    /**
     * 优雅关闭：停止 Scheduler，再断开子流，最后释放线程池
     */
    public void close() {
        try {
            // 1. 停止调度器，等待所有待发包处理完毕
            scheduler.stop();

            // 2. 关闭所有子流连接并释放 I/O 线程池
            pathManager.shutdown();

            // 3. 等待优雅关闭完成
            if (!eventLoopGroup.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                throw new TimeoutException("Shutdown timeout after " + SHUTDOWN_TIMEOUT_SECONDS + " seconds");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Shutdown interrupted", e);
        } catch (TimeoutException e) {
            throw new RuntimeException("Shutdown timeout", e);
        } finally {
            // 确保资源被清理
            eventLoopGroup.shutdownGracefully();
        }
    }
}
